#pragma once

#include <sstream>
#include "util.h"
#include "context.h"


inline void ResponseHttp(std::uint32_t request_id, const std::string& body)
{
    std::map<std::string,std::string> heads;
    log_debug("request_id:%d,data:%s", request_id, body.c_str());
    Ctx.msg_router.httpServerBack(request_id, 200, heads, body);
}

inline void ResponseHttp(std::uint32_t request_id, int err, const std::string& body)
{
    Json::Value json;
    json["err_code"] = err;
    json["Data"] = body;
    std::map<std::string,std::string> heads;
    heads["Content-Type"] = "application/json";
    Ctx.msg_router.httpServerBack(request_id, 200, heads, CTools::ToStr(json));
}

template<typename T, class=typename std::enable_if<std::is_base_of<google::protobuf::Message, T>::value>::type >
inline void httpServerBack(std::uint32_t request_id, T* msg)
{
    int status_code = 404;
    std::string body;
    if (msg != nullptr)
    {
        if (!CTools::ToJson(body, *msg))
        {
            log_error("req to json failed, req:%s", msg->ShortDebugString().c_str());
        }
        else
        {
            log_debug("req:%s", msg->ShortDebugString().c_str());
            
            status_code = 200;
        }
    }
    std::map<std::string,std::string> heads;
    heads["Content-Type"] = "application/json";
    Ctx.msg_router.httpServerBack(request_id, status_code, heads, body);
}

template<typename T, class=typename std::enable_if<std::is_base_of<google::protobuf::Message, T>::value>::type >
inline std::uint32_t httpGet(const std::string& url, T* msg)
{
    std::string json_string;
    if (msg != nullptr)
    {
        if (!CTools::ToJson(json_string, *msg))
        {
            log_error("req to json failed, req:%s", msg->ShortDebugString().c_str());
            return 0;
        }
    }

    std::map<std::string,std::string> heads;
    heads["Content-Type"] = "application/json";

    std::uint32_t callback_id = 0;
    if (!Ctx.msg_router.sendHttpGET(url, json_string, heads, callback_id) || (callback_id == 0))
    {
        log_error("sendHttpGET failed, url:%s, callback_id:%u", url.c_str(), callback_id);
        return 0;
    }
    return callback_id;
}

template<typename T, class=typename std::enable_if<std::is_base_of<google::protobuf::Message, T>::value>::type >
inline std::uint32_t httpPost(const std::string& url, T* msg)
{
    std::string json_string;
    if (msg != nullptr)
    {
        if (!CTools::ToJson(json_string, *msg))
        {
            log_error("req to json failed, req:%s", msg->ShortDebugString().c_str());
            return 0;
        }
    }

    std::map<std::string,std::string> heads;
    heads["Content-Type"] = "application/json";

    std::uint32_t callback_id = 0;
    if (!Ctx.msg_router.sendHttpPOST(url, json_string, heads, callback_id) || (callback_id == 0))
    {
        log_error("sendHttpPOST failed, url:%s, callback_id:%u", url.c_str(), callback_id);
        return 0;
    }
    return callback_id;
}

template <int server, int cmd_req, int cmd_resp, typename Req, typename Resp>
class ActionCommon
{
public:
    static bool Init(const std::string& path)
    {
        Ctx.msg_router.BindHttpHandle("post", path, ActionCommon::Handle);
        Ctx.msg_router.BindMsg(cmd_resp, ActionCommon::OnHandle);
        return true;
    }
    
public:
    static void Handle(HttpData&& http_data)
    {
        log_debug("enter,request_id:%d", http_data.request_id);
        int err = 0;
        std::stringstream body;
        do
        {
            Req req;
            if (!CTools::FromJson(req, http_data.body))
            {
                body << "json parser failed, url:" << http_data.target << ", body:" << http_data.body;
                err = 1;
                break;
            }
            Ctx.host->MtSendtoSrv(cmd_req, server, &req, 0, false, http_data.request_id);
            return;
        } while (false);
        ResponseHttp(http_data.request_id, err, body.str());
    }
    static void OnHandle(MsgData&& msg_data)
    {
        log_debug("enter,request_id:%d", msg_data.uuid);
        int err = 0;
        std::string body;
        do
        {
            Resp resp;
            if (!resp.ParseFromArray(msg_data.data.data(), msg_data.data.size()))
            {
                body = "parser proto failed";
                err = 2;
                break;
            }
            CTools::ToJson(body, resp);
        } while (false);
        ResponseHttp(msg_data.uuid, err, body);
    }
};

#define ActionRegNameCombine(a,b) a##b
#define ActionRegName(a,b) ActionRegNameCombine(a,b)
#define ActionReg(path, server, cmd_req, cmd_resp, Req, Resp) static bool ActionRegName(__init__,__LINE__) = ActionCommon<server, cmd_req, cmd_resp, Req, Resp>::Init(path)








